package com.anyext.storage.util.bytecode;

import java.io.FileOutputStream;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.springframework.asm.ClassWriter;
import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;

import com.anyext.storage.StorageContext;
import com.anyext.storage.asm.UserMapper;
import com.anyext.storage.asm.UserMapperDump;
import com.anyext.storage.jdbc.RowMapper;
import com.anyext.storage.mapping.Business;
import com.anyext.storage.mapping.SqlMap;
import com.anyext.storage.mapping.impl.BusinessImpl;
import com.anyext.storage.model.UserInfo;

public class RowMapperDump extends ClassLoader implements Opcodes {
	
	public static void main(String[] args) throws Exception {
//		System.out.println(Type.getDescriptor(Object.class));
//		System.out.println(Type.getDescriptor(RowMapper.class));
//		System.out.println(Type.getInternalName(Object.class));
//		System.out.println(Type.getInternalName(RowMapper.class));
//		System.out.println(Type.getDescriptor(ResultSet.class));
//		System.out.println(Type.getDescriptor(int.class));
		RowMapperDump loader = new RowMapperDump();
		byte[] dump = RowMapperDump.dump(null);
//		dump = UserMapperDump.dump();
		FileOutputStream fos = new FileOutputStream(UserInfo.class.getName()+"Mapper.class");
		fos.write(dump);
		fos.close();
		Class exampleClass = loader.defineClass(UserInfo.class.getName()+"Mapper", dump, 0, dump.length);
		RowMapper<UserInfo> rowMapper = (RowMapper<UserInfo>) exampleClass.newInstance();
//		UserInfo info = rowMapper.mapRow(null, 1);
//		System.out.println(info);
//		exampleClass.getMethods()[0].invoke(null, new Object[] { null, 1 });
	}

	public static byte[] dump(SqlMap sqlMap) throws Exception {
		ClassWriter cw = new ClassWriter(0);
		MethodVisitor mv;
		Label label = new Label();
//		String type = sqlMap.getResultMap().getType();
//		Business business = StorageContext.getBusiness(type);
		BusinessImpl business = new BusinessImpl();
		business.setBeanClass(UserInfo.class);
		String internalTypeName = Type.getInternalName(business.getBeanClass());//com/anyext/storage/model/UserInfo
		String typeDescriptor = Type.getDescriptor(business.getBeanClass());//Lcom/anyext/storage/model/UserInfo;
		String objectDescriptor = Type.getDescriptor(Object.class);//Ljava/lang/Object;
		String internalRowMapperName = Type.getInternalName(RowMapper.class);//com/anyext/storage/jdbc/RowMapper
		
		cw.visit(V1_6, ACC_PUBLIC + ACC_SUPER, internalTypeName+"Mapper", objectDescriptor + "L"+internalRowMapperName+"<"+typeDescriptor+">;", Type.getInternalName(Object.class), new String[]{internalRowMapperName});
		
		mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
		mv.visitVarInsn(ALOAD, 0);
		mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", "()V");
		mv.visitInsn(RETURN);
		mv.visitLocalVariable("this", "L"+internalTypeName+"Mapper;", null, label, label, 0);
		mv.visitMaxs(1, 1);
		mv.visitEnd();
		
		mv = cw.visitMethod(ACC_PUBLIC, "mapRow", "("+Type.getDescriptor(ResultSet.class)+Type.getDescriptor(int.class)+")"+typeDescriptor, null, new String[] { Type.getInternalName(SQLException.class) });
		mv.visitTypeInsn(NEW, internalTypeName);
		mv.visitInsn(DUP);
		mv.visitMethodInsn(INVOKESPECIAL, internalTypeName, "<init>", "()V");
		mv.visitVarInsn(ASTORE, 3);
		
		mv.visitVarInsn(ALOAD, 3);
		mv.visitVarInsn(ALOAD, 1);
		mv.visitLdcInsn("registerTime");
		mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(ResultSet.class), "getDate", "("+Type.getDescriptor(String.class)+")"+Type.getDescriptor(java.sql.Date.class));
		mv.visitMethodInsn(INVOKEVIRTUAL, internalTypeName, "setRegisterTime", "("+Type.getDescriptor(java.util.Date.class)+")V");
		
		mv.visitVarInsn(ALOAD, 3);
		mv.visitInsn(ARETURN);
		
		mv.visitLocalVariable("this", "L"+internalTypeName+"Mapper"+";", null, label, label, 0);
		mv.visitLocalVariable("rs", Type.getDescriptor(ResultSet.class), null, label, label, 1);
		mv.visitLocalVariable("rowNum", Type.getDescriptor(int.class), null, label, label, 2);
		mv.visitLocalVariable("business", typeDescriptor, null, label, label, 3);
		mv.visitMaxs(3, 4);
		mv.visitEnd();
		
		mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "mapRow", "("+Type.getDescriptor(ResultSet.class)+Type.getDescriptor(int.class)+")"+Type.getDescriptor(Object.class), null, new String[] { Type.getInternalName(SQLException.class) });
		mv.visitVarInsn(ALOAD, 0);
		mv.visitVarInsn(ALOAD, 1);
		mv.visitVarInsn(ILOAD, 2);
		mv.visitMethodInsn(INVOKEVIRTUAL, internalTypeName+"Mapper", "mapRow", "("+Type.getDescriptor(ResultSet.class)+Type.getDescriptor(int.class)+")"+typeDescriptor);
		mv.visitInsn(ARETURN);
		mv.visitMaxs(3, 3);
		mv.visitEnd();
		
		cw.visitEnd();
		
		return cw.toByteArray();
	}
	
}
